﻿using System;
using Microsoft.Xna.Framework;

namespace tutFuelCellGame
{
    public class LevelGenerator
    {
        private readonly FuelCellGame game;
        private Random random;

        public LevelGenerator(FuelCellGame game)
        {
            this.game = game;
            random = new Random();
        }

        public void PlaceFuelCellsAndBarriers(FuelCells fuelCells, Barriers barriers, LevelInformation currentLevel, float fuelCarrierRadius)
        {
            fuelCells.Clear();
            barriers.Clear();

            // start with the barriers.. because they are the biggest objects
            for (int index = 0; index < currentLevel.NumOfBarriers; index++)
            {
                var b = GenerateBarrier();
                b.Position = FindEmptyPosition(b, fuelCells, barriers, fuelCarrierRadius);
                b.Visible = !b.Position.Equals(Vector3.Zero);
                b.BoundingSphere = new BoundingSphere(b.Position, b.BoundingSphere.Radius);
                b.ModelBoundingParts.Transform(b.GetWorldMatrix());
                barriers.Add(b);
            }

            for (int index = 0; index < currentLevel.NumOfFuelCells; index++)
            {
                var fc = new FuelCell(game.ClockManager, game.SoundManager);
                fc.LoadContent(game.Content, GameConstants.ModelNameFuelCell);
                var p = FindEmptyPosition(fc, fuelCells, barriers, fuelCarrierRadius);
                if (!p.Equals(Vector3.Zero))
                {
                    // adjust height
                    fc.Position = new Vector3(
                        p.X,
                        fc.BoundingSphere.Radius*1.5f,
                        p.Z);

                    fc.BoundingSphere = new BoundingSphere(fc.Position,
                                                           fc.BoundingSphere.Radius);
                }
                fc.lightSource1 = game.OrangeLightSource;
                fc.lightSource2 = game.BlueLightSource;

                fuelCells.Add(fc);
            }
        }

        private Barrier GenerateBarrier()
        {
            int randomBarrier = random.Next(2);

            float scale = 0.7f + (float)random.NextDouble() * 0.3f;
            var b = new Barrier(scale)
                        {
                            Model = game.barrierModels[randomBarrier],
                            Orientation = ((float) MathHelper.ToRadians((float) random.Next(180))),
                            lightSource1 = game.OrangeLightSource,
                            lightSource2 = game.BlueLightSource
                        };
            b.Initialize(GameConstants.BarrierBoundingSphereFactor[randomBarrier]);

            return b;
        }

        private Vector3 FindEmptyPosition(GameObject gameObject, FuelCells fuelCells, Barriers barriers, float fuelCarrierRadius)
        {
            Vector3 pos = Vector3.Zero;

            int retries = 0;
            bool isOccupied;
            do
            {
                if (retries++ > 50)
                    return Vector3.Zero;

                do
                {
                    pos.X = random.Next(-80, 80);
                    pos.Z = random.Next(-80, 80);
                    gameObject.Position = pos;
                }
                while (pos.Length() < GameConstants.CenterClearRadius);

                isOccupied = fuelCells.Find(c => (c != gameObject) &&
                                                 ObjectsOverlap(c, gameObject, fuelCarrierRadius)) != null;
                if (!isOccupied)
                    isOccupied = barriers.Find(b => (b != gameObject) &&
                                                    ObjectsOverlap(b, gameObject, fuelCarrierRadius)) != null;
            } while (isOccupied);

            return pos;
        }

        private bool ObjectsOverlap(GameObject object1, GameObject object2, float fuelCarrierRadius)
        {
            var minDistance = (object1.BoundingSphere.Radius + object2.BoundingSphere.Radius);
            minDistance = MathHelper.Clamp(minDistance, fuelCarrierRadius*2.1f, minDistance);
            var dx = MathHelper.Distance(object1.Position.X, object2.Position.X);
            var dz = MathHelper.Distance(object1.Position.Z, object2.Position.Z);
            if (dx < minDistance && dz < minDistance)
            {
                return true;
            }
            return false;
        }

    }
}
